package com.yanyeori.framework.datasource.aspect;

import com.yanyeori.framework.datasource.annotation.DataSource;
import com.yanyeori.framework.datasource.config.DatabaseContextHolder;
import com.yanyeori.framework.datasource.configprop.DataSourceConfigProps;
import com.yanyeori.framework.datasource.configprop.RouterProps;
import com.yanyeori.framework.datasource.strategy.ListStrategy;
import com.yanyeori.framework.datasource.switcher.SlaveDBSwitcher;
import com.yanyeori.framework.datasource.switcher.SurviveSwitcher;
import org.apache.commons.collections4.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 动态数据源切换切面
 *
 * @author chenkuan 2020/8/7
 */
@Aspect
@Order(-1)
public class DataSourceAspect {

    private final SurviveSwitcher surviveSwitcher;
    private final DataSourceConfigProps config;
    private final ListStrategy listStrategy;

    private final Map<String, String> tempRoutersMap = new HashMap<>();

    public DataSourceAspect(SurviveSwitcher surviveSwitcher, DataSourceConfigProps config, ListStrategy listStrategy) {
        this.surviveSwitcher = surviveSwitcher;
        this.config = config;
        this.listStrategy = listStrategy;
    }

    @Pointcut("@annotation(com.yanyeori.framework.datasource.annotation.DataSource) || @within(com.yanyeori.framework.datasource.annotation.DataSource) || execution(* com..*ServiceImpl.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();

        String oldDBKey = DatabaseContextHolder.getDBKey();
        String dbKey = null;

        //获取注解指定的数据源
        DataSource annotation;
        if ((annotation = method.getAnnotation(DataSource.class)) != null) {
            dbKey = annotation.value();
        }
        if (dbKey == null && (annotation = point.getTarget().getClass().getAnnotation(DataSource.class)) != null) {
            dbKey = annotation.value();
        }
        if (dbKey == null) {
            //查询当前方法类包名是否配置了数据源路由
            dbKey = getDBKeyByRouter(point);
        }

        //若DBKey=null，则默认为默认库
        dbKey = dbKey == null ? config.getName() : dbKey;

        //处理读写分离配置
        dbKey = SlaveDBSwitcher.getInstance().getSlaveDbByPrimary(dbKey, method, config.getConfig().getSlaveDbServicePrefix(), listStrategy);

        //配置当前线程数据源
        DatabaseContextHolder.setDBKey(dbKey);

        //开启主从库存活检测及自动切换
        if (config.getConfig().isEnableMasterSlaveSwitch()) {
            surviveSwitcher.switchDB(dbKey, null);
        }

        try {
            return point.proceed();
        } finally {
            DatabaseContextHolder.setDBKey(oldDBKey);
        }
    }

    private String getDBKeyByRouter(ProceedingJoinPoint point) {
        List<RouterProps> routers = config.getRouters();
        if (CollectionUtils.isNotEmpty(routers)) {
            String packageName = point.getTarget().getClass().getName();
            String tempDbKey = tempRoutersMap.get(packageName);
            if (tempDbKey != null) {
                return tempDbKey;
            }
            for (RouterProps router : routers) {
                if (packageName.startsWith(router.getPackageLocations())) {
                    tempRoutersMap.put(packageName, router.getName());
                    return router.getName();
                }
            }
        }
        return null;
    }
}
